package com.yx.filesystem.client;

import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import com.yx.filesystem.storage.Archive;
import com.yx.filesystem.storage.SocketClient;
import com.yx.filesystem.storage.SocketServer;

public class StorageClientManager extends Thread {
	public static final long SLEEP_TIME = 60 * 1000;
	public static final long KEEP_STATE_TIME = 60 * 1000;
	private static StorageClientManager instance = null;
	
	private List<ServerNode> allServerNodeList = new ArrayList<ServerNode>();
	private List<ServerNode> aliveCanWriteServerNodeList = new ArrayList<ServerNode>();
	private List<ServerNode> aliveReadOnlyServerNodeList = new ArrayList<ServerNode>();
	private List<ServerNode> deadServerNodeList = new ArrayList<ServerNode>();
	private Hashtable<String, ServerNode> quickSearchList = new Hashtable<String, ServerNode>();

	private StorageClientManager(List<ServerNode> serverNodes) {
		if(serverNodes==null){
			this.deadServerNodeList =new ArrayList<ServerNode>();
		}else{
			this.deadServerNodeList = serverNodes;
		}
		for(int i=0;i<deadServerNodeList.size();i++){
			allServerNodeList.add(deadServerNodeList.get(i));
		}
	}

	public static void init(String[] serverIpAndPort,int TCPBuffer, int IOBuffer)
			throws Exception {
		if (TCPBuffer > 0) {
			SocketServer.SOCKET_BUFFER_SIZE = TCPBuffer * 1024;
		}
		if (IOBuffer > 0) {
			SocketServer.BUFFER_SIZE = IOBuffer * 1024;			
		}
		List<ServerNode> nodes = new ArrayList<ServerNode>();
		for (int i = 0; i < serverIpAndPort.length; i++) {
			String address = serverIpAndPort[i];
			String ip = address.substring(0, address.indexOf(":"));
			int port = Integer
					.parseInt(address.substring(address.indexOf(":") + 1));
			nodes.add(new ServerNode(ip, port));
		}
		if (instance == null) {
			instance = new StorageClientManager(nodes);
			instance.start();
		}
	}
	
	public static void addServerNode(String host,int port){
		if (instance == null) return ;
		ServerNode node=new ServerNode(host,port);
		instance.deadServerNodeList.add(node);
		instance.allServerNodeList.add(node);
	}
	public static int getAliveServerCount(boolean onlyCanWrite) {
		if (instance == null)
			return 0;
		if (onlyCanWrite) {
			return instance.aliveCanWriteServerNodeList.size();
		} else {
			return instance.aliveCanWriteServerNodeList.size()
					+ instance.aliveReadOnlyServerNodeList.size();
		}
	}

	public static List<ServerNode> getStorageServers() {
		return instance.allServerNodeList;
	}
	
	public static FileController get(String fileName) throws Exception {
		if (getAliveServerCount(false) < 1)
			throw new Exception("没有可以使用的文件存储服务器");
		if(fileName.lastIndexOf(".")>0){
			fileName=fileName.substring(0,fileName.lastIndexOf("."));
		}
		Archive fileArchive = new Archive(fileName);
		String[] nodIds=fileArchive.getNodeId();
		List<ServerNode> aliveNodes = new ArrayList<ServerNode>();
		for(int i=0;i<nodIds.length;i++){
			if (instance.quickSearchList.containsKey(nodIds[i])) {
				ServerNode serverNode = instance.quickSearchList.get(nodIds[i]);
				if (serverNode.isAlive() && serverNode.hasFile(fileArchive)) {
					aliveNodes.add(serverNode);
				}
			}
		}		
		if (aliveNodes.size() < 1) {
			throw new Exception("没有可以使用的文件备份服务器");
		}
		java.util.Random random = new java.util.Random();
		int index = random.nextInt(aliveNodes.size());
		return instance.new AliveFileController(aliveNodes.get(index),fileArchive);
	}

	public static FileController get() throws Exception {
		if (getAliveServerCount(true) < 1)
			throw new Exception("没有可以使用的文件存储服务器");
		java.util.Random random = new java.util.Random();
		int index = random.nextInt(instance.aliveCanWriteServerNodeList.size());
		ServerNode sNode=instance.aliveCanWriteServerNodeList.get(index);
		return instance.new AliveFileController(sNode,null);	
	}

	public void run() {
		while (true) {
			try {
				int aliveCanWriteLength = aliveCanWriteServerNodeList.size();
				for (int i = 0; i < aliveCanWriteLength; i++) {
					ServerNode aliveNode = aliveCanWriteServerNodeList.get(i);
					if (!aliveNode.isAlive()||aliveNode.howLong()>KEEP_STATE_TIME) {
						if (!aliveNode.heartbeat()) {
							aliveCanWriteServerNodeList.remove(i);
							deadServerNodeList.add(aliveNode);
							i--;
							aliveCanWriteLength--;
						} else {
							if (aliveNode.getState().isSpaceFull()) {
								aliveCanWriteServerNodeList.remove(i);
								aliveReadOnlyServerNodeList.add(aliveNode);
								i--;
								aliveCanWriteLength--;
							}
						}
					}
				}
				int alivereadOnlyLength = aliveReadOnlyServerNodeList.size();
				for (int i = 0; i < alivereadOnlyLength; i++) {
					ServerNode aliveNode = aliveReadOnlyServerNodeList.get(i);
					if (!aliveNode.isAlive()||aliveNode.howLong()>KEEP_STATE_TIME) {
						if (!aliveNode.heartbeat()) {
							aliveReadOnlyServerNodeList.remove(i);
							deadServerNodeList.add(aliveNode);
							i--;
							alivereadOnlyLength--;
						} else {
							if (!aliveNode.getState().isSpaceFull()) {
								aliveReadOnlyServerNodeList.remove(i);
								aliveCanWriteServerNodeList.add(aliveNode);
								i--;
								alivereadOnlyLength--;
							}
						}
					}
				}
				int deadLength = deadServerNodeList.size();
				for (int i = 0; i < deadLength; i++) {
					ServerNode deadNode = deadServerNodeList.get(i);
					boolean alwaysDead=false;
					if(deadNode.getId()==null){
						alwaysDead=true;
					}
					if (!alwaysDead&&deadNode.howLong() < KEEP_STATE_TIME)
						continue;
					if (deadNode.heartbeat()) {
						deadServerNodeList.remove(i);
						if(alwaysDead&&!quickSearchList.containsKey(deadNode.getId())){
							quickSearchList.put(deadNode.getId(), deadNode);
						}
						if (deadNode.getState().isSpaceFull()) {
							aliveReadOnlyServerNodeList.add(deadNode);
						} else {
							aliveCanWriteServerNodeList.add(deadNode);
						}
						i--;
						deadLength--;
					}
				}
			} catch (Throwable e) {
			}
			daemonSleep(SLEEP_TIME);
		}
	}

	public static void awakeDaemon() {
		if (instance == null)
			return;
		synchronized (instance) {
			instance.notify();
		}
	}

	private void daemonSleep(long time) {
		synchronized (this) {
			try {
				wait(time);
			} catch (Exception e) {
			}
		}

	}

	public class AliveFileController implements FileController {
		ServerNode serverNode = null;
		Archive fileArchive = null;

		protected AliveFileController(ServerNode serverNode, Archive fileArchive) {
			this.serverNode = serverNode;
			this.fileArchive = fileArchive;
		}

		public void read(OutputStream outPut) throws Exception {
			if (fileArchive == null)
				throw new Exception("文件名为空");
			SocketClient client = new SocketClient();
			try {
				if (!client.open(serverNode.getIp(), serverNode.getPort())) {
					serverNode.setAlive(false);
					StorageClientManager.awakeDaemon();
				}
			} catch (Throwable e) {
				serverNode.setAlive(false);
				StorageClientManager.awakeDaemon();
				client.close();
				throw new Exception("连接" + serverNode.getIp() + ":"
						+ serverNode.getPort() + "不可用！");
			}
			try {
				client.getFile(fileArchive, outPut);
			} catch (Throwable e) {
				throw new Exception("下载文件" + fileArchive.getName() + "失败："
						+ e.getMessage());
			} finally {
				client.close();
			}
		}

		public String write(String fileName,InputStream file, long length) throws Exception {
			SocketClient client = new SocketClient();
			try {
				if (!client.open(serverNode.getIp(), serverNode.getPort())) {
					serverNode.setAlive(false);
					StorageClientManager.awakeDaemon();
				}
			} catch (Throwable e) {
				serverNode.setAlive(false);
				StorageClientManager.awakeDaemon();
				client.close();
				throw new Exception("连接" + serverNode.getIp() + ":"
						+ serverNode.getPort() + "不可用！");
			}
			try {
				String starageName=client.putFile(file, length);
				return starageName+"."+getExtendName(fileName);
			} catch (Throwable e) {
				throw new Exception("上传文件失败：" + e.getMessage());
			} finally {
				client.close();
			}
		}
		
		private final String getExtendName(String fileName){
			String extName="dat";
			if(fileName.lastIndexOf(".")>0){
				extName=fileName.substring(fileName.lastIndexOf(".")+1);
			}
			return extName.trim();
		}
	}
}
